package com.example.demo.graph;

import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.KeyStrategy;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.action.AsyncEdgeAction;
import com.alibaba.cloud.ai.graph.action.AsyncNodeAction;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.List;

import static com.alibaba.cloud.ai.graph.StateGraph.END;
import static com.alibaba.cloud.ai.graph.StateGraph.START;
import com.alibaba.cloud.ai.graph.node.code.CodeExecutorNodeAction;
import com.alibaba.cloud.ai.graph.node.code.entity.CodeExecutionConfig;
import com.alibaba.cloud.ai.graph.node.code.CodeExecutor;
import com.alibaba.cloud.ai.graph.node.code.LocalCommandlineCodeExecutor;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Collectors;
import com.alibaba.cloud.ai.graph.node.AnswerNode;
import com.alibaba.cloud.ai.graph.node.KnowledgeRetrievalNode;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.ai.document.Document;
import com.alibaba.cloud.ai.graph.node.VariableAggregatorNode;
import com.alibaba.cloud.ai.graph.node.AssignerNode;
import com.alibaba.cloud.ai.graph.node.HttpNode;
import org.springframework.http.HttpMethod;
import com.alibaba.cloud.ai.graph.node.QuestionClassifierNode;
import static com.alibaba.cloud.ai.graph.action.AsyncEdgeAction.edge_async;
import com.alibaba.cloud.ai.graph.node.LlmNode;
import org.springframework.ai.chat.messages.AssistantMessage;
import com.alibaba.cloud.ai.graph.node.BranchNode;

@Component
public class GraphBuilder {
    // todo: add your knowledge base
    @Value("${rag.source:classpath:/data/}")
    private Resource ragSource;

    @Bean
    public CompiledGraph buildGraph(
        ChatModel chatModel
        , VectorStore vectorStore
        , CodeExecutionConfig codeExecutionConfig, CodeExecutor codeExecutor
        ) throws Exception {
        ChatClient chatClient = ChatClient.builder(chatModel).defaultAdvisors(new SimpleLoggerAdvisor()).build();

        // new stateGraph
        // todo: Add some non-node variable names, such as sys and conversation variables. Format as sys_xxx.
        StateGraph stateGraph = new StateGraph(() -> {
  Map<String, KeyStrategy> strategies = new HashMap<>();
  strategies.put("sys_query", (o1, o2) -> o2);
  strategies.put("knowledgeRetrievalNode2_result", (o1, o2) -> o2);
strategies.put("LLMNode5_text", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode3_result", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode1_result", (o1, o2) -> o2);
strategies.put("httpNode9_status_code", (o1, o2) -> o2);
strategies.put("codeNode15_stopHosting", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode4_result", (o1, o2) -> o2);
strategies.put("codeNode19_result", (o1, o2) -> o2);
strategies.put("codeNode19_links", (o1, o2) -> o2);
strategies.put("codeNode18_result", (o1, o2) -> o2);
strategies.put("httpNode9_files", (o1, o2) -> o2);
strategies.put("httpNode6_status_code", (o1, o2) -> o2);
strategies.put("codeNode1_character_design", (o1, o2) -> o2);
strategies.put("codeNode3_general_rules", (o1, o2) -> o2);
strategies.put("httpNode6_files", (o1, o2) -> o2);
strategies.put("startNode1_lessee", (o1, o2) -> o2);
strategies.put("httpNode7_status_code", (o1, o2) -> o2);
strategies.put("codeNode6_customer_analysis_str", (o1, o2) -> o2);
strategies.put("httpNode8_status_code", (o1, o2) -> o2);
strategies.put("codeNode3_general_rules_str", (o1, o2) -> o2);
strategies.put("httpNode9_headers", (o1, o2) -> o2);
strategies.put("LLMNode13_text", (o1, o2) -> o2);
strategies.put("httpNode3_body", (o1, o2) -> o2);
strategies.put("codeNode4_filtered_product_str", (o1, o2) -> o2);
strategies.put("httpNode3_files", (o1, o2) -> o2);
strategies.put("LLMNode6_text", (o1, o2) -> o2);
strategies.put("codeNode13_otherInfo", (o1, o2) -> o2);
strategies.put("LLMNode15_text", (o1, o2) -> o2);
strategies.put("httpNode5_body", (o1, o2) -> o2);
strategies.put("httpNode2_body", (o1, o2) -> o2);
strategies.put("httpNode6_headers", (o1, o2) -> o2);
strategies.put("image_analysis", (o1, o2) -> o2);
strategies.put("LLMNode12_text", (o1, o2) -> o2);
strategies.put("httpNode2_headers", (o1, o2) -> o2);
strategies.put("startNode1_message_id", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode9_result", (o1, o2) -> o2);
strategies.put("httpNode1_files", (o1, o2) -> o2);
strategies.put("httpNode3_status_code", (o1, o2) -> o2);
strategies.put("httpNode1_status_code", (o1, o2) -> o2);
strategies.put("codeNode2_special_scenarios_str", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode7_result", (o1, o2) -> o2);
strategies.put("codeNode11_result", (o1, o2) -> o2);
strategies.put("codeNode4_product_names_list", (o1, o2) -> o2);
strategies.put("httpNode2_status_code", (o1, o2) -> o2);
strategies.put("codeNode17_result", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode5_result", (o1, o2) -> o2);
strategies.put("codeNode6_custom_info", (o1, o2) -> o2);
strategies.put("httpNode5_status_code", (o1, o2) -> o2);
strategies.put("codeNode19_count", (o1, o2) -> o2);
strategies.put("httpNode4_status_code", (o1, o2) -> o2);
strategies.put("startNode1_server_conversation_id", (o1, o2) -> o2);
strategies.put("codeNode8_collect_product_info_str", (o1, o2) -> o2);
strategies.put("httpNode4_files", (o1, o2) -> o2);
strategies.put("LLMNode8_text", (o1, o2) -> o2);
strategies.put("codeNode16_quotableProductNames", (o1, o2) -> o2);
strategies.put("codeNode7_result", (o1, o2) -> o2);
strategies.put("LLMNode10_text", (o1, o2) -> o2);
strategies.put("LLMNode2_text", (o1, o2) -> o2);
strategies.put("codeNode13_customerDemand", (o1, o2) -> o2);
strategies.put("codeNode5_lessee_tags_str", (o1, o2) -> o2);
strategies.put("LLMNode3_text", (o1, o2) -> o2);
strategies.put("LLMNode16_text", (o1, o2) -> o2);
strategies.put("collect_product_info", (o1, o2) -> o2);
strategies.put("collect_product_info_str", (o1, o2) -> o2);
strategies.put("httpNode6_body", (o1, o2) -> o2);
strategies.put("LLMNode9_text", (o1, o2) -> o2);
strategies.put("startNode1_customer_name", (o1, o2) -> o2);
strategies.put("codeNode12_productScore", (o1, o2) -> o2);
strategies.put("httpNode5_files", (o1, o2) -> o2);
strategies.put("httpNode7_headers", (o1, o2) -> o2);
strategies.put("codeNode13_customerInfo", (o1, o2) -> o2);
strategies.put("codeNode15_id", (o1, o2) -> o2);
strategies.put("codeNode2_special_scenarios", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode6_result", (o1, o2) -> o2);
strategies.put("httpNode3_headers", (o1, o2) -> o2);
strategies.put("codeNode18_count", (o1, o2) -> o2);
strategies.put("LLMNode21_text", (o1, o2) -> o2);
strategies.put("httpNode9_body", (o1, o2) -> o2);
strategies.put("answerNode2_answer", (o1, o2) -> o2);
strategies.put("answerNode1_answer", (o1, o2) -> o2);
strategies.put("LLMNode19_text", (o1, o2) -> o2);
strategies.put("codeNode18_links", (o1, o2) -> o2);
strategies.put("LLMNode18_text", (o1, o2) -> o2);
strategies.put("httpNode10_headers", (o1, o2) -> o2);
strategies.put("codeNode11_total_score", (o1, o2) -> o2);
strategies.put("httpNode8_body", (o1, o2) -> o2);
strategies.put("codeNode1_character_design_str", (o1, o2) -> o2);
strategies.put("LLMNode20_text", (o1, o2) -> o2);
strategies.put("httpNode10_files", (o1, o2) -> o2);
strategies.put("startNode1_source", (o1, o2) -> o2);
strategies.put("codeNode4_product_names_str", (o1, o2) -> o2);
strategies.put("httpNode7_files", (o1, o2) -> o2);
strategies.put("codeNode12_roundScore", (o1, o2) -> o2);
strategies.put("questionClassifyNode1_class_name", (o1, o2) -> o2);
strategies.put("variableAggregatorNode1_output", (o1, o2) -> o2);
strategies.put("stop_hosting", (o1, o2) -> o2);
strategies.put("codeNode6_user_tags_str", (o1, o2) -> o2);
strategies.put("httpNode4_headers", (o1, o2) -> o2);
strategies.put("codeNode15_reason", (o1, o2) -> o2);
strategies.put("codeNode9_result", (o1, o2) -> o2);
strategies.put("codeNode5_lessee_tags", (o1, o2) -> o2);
strategies.put("codeNode4_filtered_product_data", (o1, o2) -> o2);
strategies.put("codeNode12_totalScore", (o1, o2) -> o2);
strategies.put("codeNode12_conclusion", (o1, o2) -> o2);
strategies.put("httpNode8_headers", (o1, o2) -> o2);
strategies.put("httpNode10_status_code", (o1, o2) -> o2);
strategies.put("sceneId", (o1, o2) -> o2);
strategies.put("httpNode8_files", (o1, o2) -> o2);
strategies.put("httpNode5_headers", (o1, o2) -> o2);
strategies.put("codeNode8_collect_product_info", (o1, o2) -> o2);
strategies.put("LLMNode7_text", (o1, o2) -> o2);
strategies.put("httpNode10_body", (o1, o2) -> o2);
strategies.put("httpNode1_headers", (o1, o2) -> o2);
strategies.put("httpNode7_body", (o1, o2) -> o2);
strategies.put("knowledgeRetrievalNode8_result", (o1, o2) -> o2);
strategies.put("httpNode4_body", (o1, o2) -> o2);
strategies.put("httpNode2_files", (o1, o2) -> o2);
strategies.put("LLMNode17_text", (o1, o2) -> o2);
strategies.put("codeNode14_result", (o1, o2) -> o2);
strategies.put("LLMNode4_text", (o1, o2) -> o2);
strategies.put("LLMNode1_text", (o1, o2) -> o2);
strategies.put("LLMNode14_text", (o1, o2) -> o2);
strategies.put("LLMNode11_text", (o1, o2) -> o2);
strategies.put("codeNode10_result", (o1, o2) -> o2);
strategies.put("httpNode1_body", (o1, o2) -> o2);
strategies.put("variableAggregatorNode2_output", (o1, o2) -> o2);
strategies.put("codeNode12_round", (o1, o2) -> o2);
  return strategies;
}
);
        // add nodes
        // —— HttpNode [17557641927460] ——
HttpNode httpNode1 = HttpNode.builder()
.method(HttpMethod.POST)
.url("{{#env.test#}}/lessee/inner/roleSetting/get")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-3362\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n  \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n  \\\"lesseeId\\\": \\\"${startNode1_lessee}\\\"\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode1_body")
.build();
stateGraph.addNode("httpNode1", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode1";
   String key = varName + "_body";
   Map<String, Object> result = httpNode1.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— HttpNode [17557656415320] ——
HttpNode httpNode2 = HttpNode.builder()
.method(HttpMethod.POST)
.url("{{#env.test#}}/notice/inner/sceneList")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-3513\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n  \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n  \\\"lesseeId\\\": ${startNode1_lessee}\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode2_body")
.build();
stateGraph.addNode("httpNode2", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode2";
   String key = varName + "_body";
   Map<String, Object> result = httpNode2.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [1755767729755] ——
CodeExecutorNodeAction codeNode1 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    if not arg1:
        return {"character_design": {}, "character_design_str": "暂无角色设定"}

    try:
        response_data = json.loads(arg1)
    except json.JSONDecodeError:
        return {
            "character_design": {},
            "character_design_str": "输入的角色设定不是合法的 JSON",
        }

    data = response_data.get("data", {}) if isinstance(response_data, dict) else {}

    template = (
        "你是{name}，性别{sex}，使用的语言是{chatLang}。"
        "你的交流风格是{chatStyle}，并且精通{skill}。"
        "你现在正在作为{companyName}的销售人员，与客户沟通，该公司的主营产品包括{mainProducts}。"
        "目标客户群体主要是{customerGroup}。"
        "在对话中，你需要保持自然、亲切，同时展现专业度。"
        "回答要生动、有趣，并适当地融入{skill}文化元素，让客户感受到轻松和信任。"
        "在理解客户需求的基础上，自然地推荐合适的产品，强调这些产品如何符合客户的兴趣和风格。"
    )

    data_str = template.format(
        name=data.get("name", "未命名角色"),
        sex=data.get("sex", "未知"),
        skill=data.get("skill", "暂无技能"),
        companyName=data.get("companyName", "某公司"),
        mainProducts=data.get("mainProducts", "相关产品"),
        customerGroup=data.get("customerGroup", "客户群体"),
        chatStyle=data.get("chatStyle", "自然随和"),
        chatLang=data.get("chatLang", "英文"),
    )

    return {"character_design": data, "character_design_str": data_str}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode1_body"))
.outputKey("codeNode1_output")
    .build();
stateGraph.addNode("codeNode1", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode1.apply(state);
            String key = "codeNode1_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode1_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— CodeNode [1755767751197] ——
CodeExecutorNodeAction codeNode2 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    if not arg1:
        return {"special_scenarios": [], "special_scenarios_str": "暂无特殊场景"}

    try:
        response_data = json.loads(arg1)
    except json.JSONDecodeError:
        return {
            "special_scenarios": [],
            "special_scenarios_str": "输入的内容不是合法的 JSON",
        }

    data = response_data.get("data", [])
    cleaned_data = [dict(item) for item in data if isinstance(item, dict)]

    data_str = (
        json.dumps(cleaned_data, ensure_ascii=False, indent=2)
        if cleaned_data
        else "暂无特殊场景"
    )

    return {"special_scenarios": cleaned_data, "special_scenarios_str": data_str}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode2_body"))
.outputKey("codeNode2_output")
    .build();
stateGraph.addNode("codeNode2", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode2.apply(state);
            String key = "codeNode2_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode2_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— HttpNode [17557683471520] ——
HttpNode httpNode3 = HttpNode.builder()
.url("{{#env.test#}}/api/sale-rule/query/{{#1755760119759.lessee#}}")
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode3_body")
.build();
stateGraph.addNode("httpNode3", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode3";
   String key = varName + "_body";
   Map<String, Object> result = httpNode3.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [1755768706621] ——
CodeExecutorNodeAction codeNode3 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    if not arg1:
        return {"general_rules": [], "general_rules_str": "暂无通用规则"}

    try:
        response_data = json.loads(arg1)
    except json.JSONDecodeError:
        return {"general_rules": [], "general_rules_str": "输入的内容不是合法的 JSON"}

    data = response_data.get("data", [])
    rules_list = [
        {"id": item.get("id", ""), "ruleContent": item.get("ruleContent", "")}
        for item in data
        if isinstance(item, dict)
    ]

    rules_text = (
        "\\n".join(
            f"{i + 1}. {item['ruleContent']}" for i, item in enumerate(rules_list)
        )
        if rules_list
        else "暂无通用规则"
    )

    return {"general_rules": rules_list, "general_rules_str": rules_text}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode3_body"))
.outputKey("codeNode3_output")
    .build();
stateGraph.addNode("codeNode3", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode3.apply(state);
            String key = "codeNode3_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode3_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— HttpNode [17557763591460] ——
HttpNode httpNode4 = HttpNode.builder()
.url("{{#env.test#}}/api/sale-plan/query/all")
.queryParam("lesseeId", "${startNode1_lessee}")
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode4_body")
.build();
stateGraph.addNode("httpNode4", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode4";
   String key = varName + "_body";
   Map<String, Object> result = httpNode4.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [1755776428456] ——
CodeExecutorNodeAction codeNode4 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    if not arg1:
        return {
            "filtered_product_data": [],
            "product_names_list": [],
            "product_names_str": "",
            "filtered_product_str": "",
        }

    data = json.loads(arg1).get("data", [])
    filtered_output, product_names_list, filtered_product_list = [], [], []

    for product in data:
        product_name = product.get("productName", "")
        attributes = product.get("attributes", [])

        filtered_attrs = []
        for attr in attributes:
            label = attr.get("label", "")
            raw_desc = attr.get("description", "")
            option_text = f"（可选的值有{raw_desc}）" if raw_desc else ""

            description_text = f"现在客户想要【{product_name}】，请询问客户对于【{product_name}】的【{label}】需求{option_text}"

            filtered_attrs.append(
                {
                    "label": label,
                    "description": description_text,
                    "score": attr.get("score", 10),
                    "value": attr.get("value", ""),
                    "reason": "",
                }
            )

        if filtered_attrs:
            filtered_output.append(
                {
                    "productName": product_name,
                    "thresholdScore": product.get("thresholdScore", 50),
                    "totalScore": product.get("totalScore"),
                    "isQuotable": product.get("isQuotable", 0),
                    "attributes": filtered_attrs,
                }
            )

        if product_name:
            product_names_list.append(product_name)

        simple_attrs = [
            {"label": attr.get("label", ""), "description": attr.get("description", "")}
            for attr in attributes
        ]
        if simple_attrs:
            filtered_product_list.append(
                {"productName": product_name, "attributes": simple_attrs}
            )

    return {
        "filtered_product_data": filtered_output,
        "product_names_list": product_names_list,
        "product_names_str": json.dumps(product_names_list, ensure_ascii=False)
        if product_names_list
        else "",
        "filtered_product_str": json.dumps(filtered_product_list, ensure_ascii=False)
        if filtered_product_list
        else "",
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode4_body"))
.outputKey("codeNode4_output")
    .build();
stateGraph.addNode("codeNode4", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode4.apply(state);
            String key = "codeNode4_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode4_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— HttpNode [17558307127980] ——
HttpNode httpNode5 = HttpNode.builder()
.method(HttpMethod.POST)
.url("{{#env.test#}}/lessee/inner/lesseeTags/get")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-4065\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n    \\\"apiKey\\\": ${env_app_key},\\n    \\\"lesseeId\\\": ${startNode1_lessee}\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode5_body")
.build();
stateGraph.addNode("httpNode5", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode5";
   String key = varName + "_body";
   Map<String, Object> result = httpNode5.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [17558307242790] ——
CodeExecutorNodeAction codeNode5 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    if not arg1:
        return {"lessee_tags": [], "lessee_tags_str": "[]"}

    try:
        response_data = json.loads(arg1)
    except json.JSONDecodeError:
        return {"lessee_tags": [], "lessee_tags_str": "[]"}

    data = response_data.get("data", [])
    lessee_tags = [
        {"tagId": item.get("tagId", ""), "tagName": item.get("tagName", "")}
        for item in data
        if isinstance(item, dict)
    ]

    lessee_tags_str = json.dumps(lessee_tags, ensure_ascii=False)

    return {"lessee_tags": lessee_tags, "lessee_tags_str": lessee_tags_str}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode5_body"))
.outputKey("codeNode5_output")
    .build();
stateGraph.addNode("codeNode5", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode5.apply(state);
            String key = "codeNode5_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode5_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— BranchNode [1755849694052] ——
stateGraph.addNode("branchNode1", AsyncNodeAction.node_async(state -> Map.of()));

// —— LlmNode [1755850441014] ——
LlmNode LLMNode1 = LlmNode.builder()
.systemPromptTemplate("角色设定：{codeNode1_character_design_str}\n任务是判断用户消息是否触发特殊场景规则。\n\n输入：\n- 特殊场景：{codeNode2_special_scenarios_str}\n- 商家在售商品：{codeNode4_filtered_product_str}\n\n规则：\n1. 输出必须严格符合 JSON Schema。\n2. 最外层必须是一个对象，包含唯一字段 triggeredScenes。\n3. triggeredScenes 的值是一个数组，数组中每个元素包含：id、sceneName、sceneDesc、stopHosting、reason。\n4. 如果没有触发，返回 {\"triggeredScenes\": []}。\n5. 禁止输出用户消息内容或其他文字，只输出 JSON。")
.userPromptTemplate("{sys_query}")
.params(Map.of("sys_query", "null", "codeNode2_special_scenarios_str", "null", "codeNode1_character_design_str", "null", "codeNode4_filtered_product_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode1_text")
.build();
stateGraph.addNode("LLMNode1", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode1.apply(state);
	String key = "LLMNode1_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— HttpNode [1755853472970] ——
HttpNode httpNode6 = HttpNode.builder()
.method(HttpMethod.POST)
.url("{{#env.test#}}/notice/inner/pushAlertMsg")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-109\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n  \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n  \\\"sceneId\\\": ${conversation_sceneId},\\n  \\\"lesseeId\\\": ${startNode1_lessee},\\n  \\\"imUserId\\\": ${sys_user_id},\\n  \\\"msgId\\\": ${startNode1_message_id},\\n  \\\"conversationId\\\": \\\"${startNode1_server_conversation_id}\\\",\\n  \\\"query\\\": \\\"${sys_query}\\\",\\n  \\\"message\\\": \\\"${variableAggregatorNode1_output}\\\",\\n  \\\"reason\\\": \\\"${codeNode15_reason}\\\"\\n}/\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode6_body")
.build();
stateGraph.addNode("httpNode6", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode6";
   String key = varName + "_body";
   Map<String, Object> result = httpNode6.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— LlmNode [1755854546432] ——
LlmNode LLMNode2 = LlmNode.builder()
.systemPromptTemplate("你是一个智能助手，任务是根据输入信息生成客户简要画像。\n\n⚠️ 规则（必须遵守）：\n1. 输出必须严格是一个 JSON 数组。\n2. 数组中只包含一个对象。\n3. 对象必须包含以下三个字段：customerInfo、customerDemand、otherInfo。\n4. 输出中禁止出现任何非 JSON 内容（包括自然语言解释、注释或额外文字）。\n\n输入信息：\n- 客户之前的总结分析：{codeNode6_customer_analysis_str}\n- 客户需求商品：{conversation_collect_product_info_str}\n\n✅ 输出示例（注意：只能输出这种 JSON 结构，不能有多余文字）：\n[\n  {\n    \"customerInfo\": \"客户为年轻职场新人，追求高性价比和实用性\",\n    \"customerDemand\": \"需要购买一款入门级智能手机\",\n    \"otherInfo\": \"客户比较关注价格和售后服务\"\n  }\n]")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode6_customer_analysis_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode2_text")
.build();
stateGraph.addNode("LLMNode2", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode2.apply(state);
	String key = "LLMNode2_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— HttpNode [17558556503520] ——
HttpNode httpNode7 = HttpNode.builder()
.method(HttpMethod.POST)
.url("{{#env.test#}}/lessee/inner/customInfos/get")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-8343\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n    \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n    \\\"lesseeId\\\": ${startNode1_lessee},\\n    \\\"imUserId\\\": ${sys_user_id}\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode7_body")
.build();
stateGraph.addNode("httpNode7", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode7";
   String key = varName + "_body";
   Map<String, Object> result = httpNode7.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— HttpNode [1755856286673] ——
HttpNode httpNode8 = HttpNode.builder()
.method(HttpMethod.POST)
.url("{{#env.test#}}/lessee/inner/customerAnalyze/set")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-1500\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n  \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n  \\\"lesseeId\\\": ${startNode1_lessee},\\n  \\\"imUserId\\\": ${sys_user_id},\\n  \\\"customerInfo\\\": ${codeNode13_customerInfo},\\n  \\\"customerDemand\\\": ${codeNode13_customerDemand},\\n  \\\"otherInfo\\\": ${codeNode13_otherInfo}\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode8_body")
.build();
stateGraph.addNode("httpNode8", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode8";
   String key = varName + "_body";
   Map<String, Object> result = httpNode8.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— LlmNode [1755856306126] ——
LlmNode LLMNode3 = LlmNode.builder()
.systemPromptTemplate("你是一个智能助手，任务是为客户打标签。\n\n输入：\n- 客户需求商品：{conversation_collect_product_info_str}\n- 可选标签列表：{codeNode5_lessee_tags_str}\n- 客户已有标签：{codeNode6_user_tags_str}\n\n规则：\n1. 只能从“可选标签列表”中选择标签，禁止生成新标签。\n2. 输出结果必须为 JSON，对象中必须有一个字段 \"tags_list\"。\n3. \"tags_list\" 的值是数组，数组内的每个对象必须包含 \"tagId\" 字段。\n4. 如果没有新增标签，输出 `{ \"tags_list\": [] }`。\n5. 严禁输出任何 JSON 以外的内容（包括解释、自然语言、注释）。\n\n输出格式示例：\n{\n  \"tags_list\": [\n    { \"tagId\": \"12345\" },\n    { \"tagId\": \"67890\" }\n  ]\n}")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode5_lessee_tags_str", "null", "codeNode6_user_tags_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode3_text")
.build();
stateGraph.addNode("LLMNode3", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode3.apply(state);
	String key = "LLMNode3_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— HttpNode [1755857078513] ——
HttpNode httpNode9 = HttpNode.builder()
.method(HttpMethod.POST)
.url("{{#env.test#}}/api/im/setImUserTag")
.body(HttpNode.HttpRequestNodeBody.fromJson("{\"data\":[{\"id\":\"key-value-9063\",\"key\":\"\",\"type\":\"text\",\"value\":\"{\\n    \\\"apiKey\\\": \\\"${env_app_key}\\\",\\n    \\\"imUserId\\\": ${sys_user_id},\\n    \\\"tagIds\\\": ${codeNode7_result}\\n}\"}],\"type\":\"json\"}"))
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode9_body")
.build();
stateGraph.addNode("httpNode9", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode9";
   String key = varName + "_body";
   Map<String, Object> result = httpNode9.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [1755916796748] ——
CodeExecutorNodeAction codeNode6 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    try:
        data = json.loads(arg1)
    except json.JSONDecodeError:
        data = {}

    user_data = data.get("data", {})

    customer_fields = ["customerInfo", "customerDemand", "otherInfo"]
    customer_data = {k: user_data[k] for k in customer_fields if k in user_data}

    user_tags_str = json.dumps(user_data.get("tags", []), ensure_ascii=False)

    return {
        "custom_info": user_data,
        "customer_analysis_str": json.dumps(customer_data, ensure_ascii=False),
        "user_tags_str": user_tags_str,
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode7_body"))
.outputKey("codeNode6_output")
    .build();
stateGraph.addNode("codeNode6", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode6.apply(state);
            String key = "codeNode6_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode6_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— CodeNode [1755942129724] ——
CodeExecutorNodeAction codeNode7 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: dict) -> dict:
    return {"result": json.dumps(arg1)}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "LLMNode3_structured_output"))
.outputKey("codeNode7_output")
    .build();
stateGraph.addNode("codeNode7", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode7.apply(state);
            String key = "codeNode7_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode7_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— CodeNode [1755945926794] ——
CodeExecutorNodeAction codeNode8 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
import copy

def main(arg1: list, arg2: list[dict], arg3: list[dict]):
    product_map = {
        item["productName"]: item
        for item in arg2
        if isinstance(item, dict) and "productName" in item
    }

    updated_arg3 = copy.deepcopy(arg3) if arg3 else []

    for i, item in enumerate(updated_arg3):
        product_name = item.get("product")
        ref_product = product_map.get(product_name)
        if not ref_product:
            continue

        ref_attrs_map = {a["label"]: a for a in ref_product.get("attributes", [])}
        new_attrs = []
        for attr in item.get("attributes", []):
            label = attr.get("label")
            if label not in ref_attrs_map:
                continue

            ref_attr = ref_attrs_map[label]
            merged_attr = attr.copy()

            for key in ["label", "description", "score"]:
                merged_attr[key] = ref_attr.get(key, merged_attr.get(key))

            merged_attr["reason"] = merged_attr.get("reason") or ref_attr.get(
                "reason", ""
            )

            merged_attr["value"] = merged_attr.get("value", "")

            new_attrs.append(merged_attr)

        updated_arg3[i]["attributes"] = new_attrs

    if arg1:
        collected_products = {item.get("product") for item in updated_arg3}
        for product_name in arg1:
            product_name = product_name.strip()
            if product_name in collected_products:
                continue
            ref_product = product_map.get(product_name)
            if not ref_product:
                continue

            new_attrs = []
            for attr in ref_product.get("attributes", []):
                attr_copy = attr.copy()
                attr_copy["value"] = ""
                attr_copy["reason"] = ""
                attr_copy["score"] = attr_copy.get("score", 10)
                new_attrs.append(attr_copy)

            updated_arg3.append(
                {
                    "product": product_name,
                    "thresholdScore": ref_product.get("thresholdScore", 50),
                    "totalScore": ref_product.get("totalScore", 50),
                    "isQuotable": ref_product.get("isQuotable", 0),
                    "attributes": new_attrs,
                }
            )

    return {
        "collect_product_info": updated_arg3,
        "collect_product_info_str": json.dumps(updated_arg3, ensure_ascii=False),
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "LLMNode5_structured_output", "arg2", "codeNode4_filtered_product_data", "arg3", "conversation_collect_product_info"))
.outputKey("codeNode8_output")
    .build();
stateGraph.addNode("codeNode8", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode8.apply(state);
            String key = "codeNode8_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode8_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— LlmNode [1755947932458] ——
LlmNode LLMNode4 = LlmNode.builder()
.systemPromptTemplate("这是客户可能需要的商品信息：\n{conversation_collect_product_info_str}\n这是用户可能上传的图片信息：\n{conversation_image_analysis}\n\n规则：\n1. 只能输出 JSON，不允许输出任何多余文字、解释、总结或问题。\n2. 如果商品信息为空，请输出空数组：\n   {\"product_information\": []}\n3. 从上下文中提取商品属性填入对应 value：\n   - 找到对应值则填入 value\n   - 未找到则 value 为 \"\"\n   - 给出判断理由，填入reason字段\n4. 输出 JSON 必须完全符合 Schema，禁止任何额外字段。\n5. 不允许自然语言回答，不允许中文说明或提示。")
.params(Map.of("conversation_collect_product_info_str", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode4_text")
.build();
stateGraph.addNode("LLMNode4", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode4.apply(state);
	String key = "LLMNode4_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— AssignerNode [1755948713597] ——
AssignerNode assignerNode1 = AssignerNode.builder()
.addItem("conversation_collect_product_info_str", "codeNode9_result", AssignerNode.WriteMode.OVER_WRITE)
.addItem("conversation_collect_product_info", "LLMNode4_structured_output", AssignerNode.WriteMode.OVER_WRITE)
.build();
stateGraph.addNode("assignerNode1", AsyncNodeAction.node_async(assignerNode1));

// —— LlmNode [1755949477160] ——
LlmNode LLMNode5 = LlmNode.builder()
.systemPromptTemplate("你是智能助手，任务是判断用户是否对在售商品有潜在购买需求。\n\n输入：\n- 在售商品列表：{codeNode4_product_names_str}\n- 已收集需求商品：{conversation_collect_product_info_str}\n- 图片文本描述：{conversation_image_analysis}\n\n规则：\n1. 优先使用图片文本描述匹配在售商品（完全匹配或包含关键词）。\n2. 如果图片匹配不到，再使用已收集需求商品匹配在售商品。\n3. 只能从在售商品列表中选择，禁止生成新商品名称。\n4. 输出必须严格为单个 JSON 对象，字段顺序必须为：\n   1. confidence\n   2. new_products\n   3. purchase_intent\n   4. reason\n5. 如果没有匹配到商品：\n   - new_products 输出 []\n   - purchase_intent 输出 \"无\"\n   - confidence 输出 0.0\n   - reason 输出简短说明\n6. **禁止输出任何 JSON 以外的内容，包括自然语言、解释、注释或 Markdown。**\n\n请根据输入严格输出 JSON 对象，不允许任何额外内容。")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode4_product_names_str", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode5_text")
.build();
stateGraph.addNode("LLMNode5", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode5.apply(state);
	String key = "LLMNode5_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— CodeNode [17560179385050] ——
CodeExecutorNodeAction codeNode9 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: dict) -> dict:
    if not arg1:
        return {"result": ""}
    return {
        "result": json.dumps(arg1, ensure_ascii=False),
    }

""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "LLMNode4_structured_output"))
.outputKey("codeNode9_output")
    .build();
stateGraph.addNode("codeNode9", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode9.apply(state);
            String key = "codeNode9_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode9_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— LlmNode [1756018471542] ——
LlmNode LLMNode6 = LlmNode.builder()
.systemPromptTemplate("Role: {codeNode1_character_design_str}\n\nInputs:\n Merchant rules: {codeNode3_general_rules_str} (must always follow)\n Sales stage: {codeNode14_result} (suggested sales phrases and approach)\n Sales strategy: {codeNode10_result} (guides conversation naturally)\n Customer intended products: {conversation_collect_product_info_str} (customer product needs/preferences)\n Product/resource database: {{#context#}} (contains product descriptions and links)\n User uploaded images: {conversation_image_analysis} (optional visual reference)\n Reference scripts: {LLMNode15_text} (optional sales phrases)\n\nTask:\n1. Always follow Merchant rules strictly.\n2. Use Sales stage and Sales strategy as guidance to talk naturally.\n3. If customer input matches Reference scripts, prioritize those responses.\n4. Ask only about missing product attributes from Customer intended products, in logical order.\n5. First sentence must answer core info or ask the key question.\n6. Reply in English, concise (≤40 words), direct, single-paragraph style.\n7. Separate each sentence or link with ###, but do not start the message with ###.\n8. Provide at least 5 real https product links; multiple links per product allowed; each ends with ###.\n9. If no specific product is given, recommend a diverse set of products from the database (push more links).\n10. If a product is given but attributes are missing (e.g. color, size, model), ask about them and recommend multiple related product links or variants.\n11. Do not fabricate information, links, or prices.\n12. Keep a natural, human-like conversational tone.\n\nProhibitions:\n- 40 words\n- Incomplete sentences\n- Consecutive questions\n- Repeated or similar questions\n- Bullet points\n- Fabricated info, links, or prices")
.params(Map.of("codeNode3_general_rules_str", "null", "conversation_collect_product_info_str", "null", "LLMNode15_text", "null", "codeNode1_character_design_str", "null", "codeNode14_result", "null", "codeNode10_result", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode6_text")
.build();
stateGraph.addNode("LLMNode6", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode6.apply(state);
	String key = "LLMNode6_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— LlmNode [1756019111848] ——
LlmNode LLMNode7 = LlmNode.builder()
.systemPromptTemplate("你是一个情商极高的销售，根据用户问题回答")
.userPromptTemplate("{sys_query}")
.params(Map.of("sys_query", "null"))
.chatClient(chatClient)
.outputKey("LLMNode7_text")
.build();
stateGraph.addNode("LLMNode7", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode7.apply(state);
	String key = "LLMNode7_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— CodeNode [1756019266673] ——
CodeExecutorNodeAction codeNode10 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
import re
def main(arg1: str) -> dict:
    tags = ["summary", "think"]
    for tag in tags:
        matches = re.findall(
            rf"<{tag}>(.*?)</{tag}>",
            arg1,
            re.DOTALL | re.IGNORECASE,
        )
        for content in matches:
            content = content.strip()
            if content:
                return {"result": content}
    return {"result": arg1}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "LLMNode7_text"))
.outputKey("codeNode10_output")
    .build();
stateGraph.addNode("codeNode10", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode10.apply(state);
            String key = "codeNode10_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode10_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— CodeNode [1756020347663] ——
CodeExecutorNodeAction codeNode11 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(
    arg1: list[dict], arg2: float, arg3: float, arg4: float, arg5: float, arg6: float
) -> dict:
    result = []
    bonus = arg5 if arg3 == arg4 else 0
    global_score = 0
    hit_count = 0

    for product in arg1:
        product_score = 0
        filtered_attrs = []

        for attr in product.get("attributes", []):
            if attr.get("value"):
                product_score += attr.get("score", 0)
                filtered_attrs.append(
                    {k: v for k, v in attr.items() if k not in ("score",)}
                )

        global_score += product_score
        if filtered_attrs:
            hit_count += 1
            result.append(
                {
                    "productName": product.get("productName"),
                    "attributes": filtered_attrs,
                }
            )

    total_score = global_score + bonus + hit_count * arg6
    return {"result": result if total_score >= arg2 else [], "total_score": total_score}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "conversation_collect_product_info", "arg2", "codeNode12_totalScore", "arg3", "sys_dialogue_count", "arg4", "codeNode12_round", "arg5", "codeNode12_roundScore", "arg6", "codeNode12_productScore"))
.outputKey("codeNode11_output")
    .build();
stateGraph.addNode("codeNode11", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode11.apply(state);
            String key = "codeNode11_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode11_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— BranchNode [1756020912920] ——
stateGraph.addNode("branchNode2", AsyncNodeAction.node_async(state -> Map.of()));

// —— LlmNode [1756021890832] ——
LlmNode LLMNode8 = LlmNode.builder()
.systemPromptTemplate("请生成一条正式且温和的结束语：\n结束语模板：{codeNode12_conclusion}\n\n要求：\n1. 如果模板有值，用意思相近的英文表达；\n2. 如果模板为空，自由发挥，用英文表达。\n3. 内容意思为“我们正在处理您的请求，请耐心等待”，用词简洁自然，避免与常见表达完全雷同。")
.params(Map.of("codeNode12_conclusion", "null"))
.chatClient(chatClient)
.outputKey("LLMNode8_text")
.build();
stateGraph.addNode("LLMNode8", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode8.apply(state);
	String key = "LLMNode8_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— AnswerNode [1756021965125] ——
AnswerNode answerNode1 = AnswerNode.builder()
.answer("{LLMNode8_text}")
.outputKey("answerNode1_answer")
.build();
stateGraph.addNode("answerNode1", AsyncNodeAction.node_async(answerNode1));

stateGraph.addEdge("answerNode1", END);
// —— LlmNode [1756028965021] ——
LlmNode LLMNode9 = LlmNode.builder()
.systemPromptTemplate("You are an intelligent assistant. Your task is to generate a structured JSON description based on the user-uploaded image.\n\nInput:\n- User-uploaded image content (text description)\n- List of available product categories: {codeNode4_product_names_list}\n- Available product attributes: {codeNode4_filtered_product_str}\n\nRules:\n1. First, describe the image content in detail, including scene, objects, colors, actions, and other key elements.  \n2. Determine whether the image content is related to any available product category:  \n   - If related: output the image description + the corresponding product category.  \n   - If not related: output the image description and set the product category to \"No related product category\".  \n3. **Do not answer user questions or generate extra text. Only output JSON.**")
.userPromptTemplate("{sys_query}")
.params(Map.of("sys_query", "null", "codeNode4_product_names_list", "null", "codeNode4_filtered_product_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode9_text")
.build();
stateGraph.addNode("LLMNode9", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode9.apply(state);
	String key = "LLMNode9_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— LlmNode [1756029346700] ——
LlmNode LLMNode10 = LlmNode.builder()
.systemPromptTemplate("任务：根据输入的商品信息和图片描述，生成一个**全英文**、**单行**、**小写**、**中横线分隔（kebab-case）**的搜索查询词。禁止输出任何解释或多余内容，只输出最终 slug。\n\n输入：\n- 商品信息：{conversation_collect_product_info_str}\n- 图片描述：{conversation_image_analysis}\n- 在售商品：{codeNode4_product_names_list}\n\n规则：\n1. 若 productName 或属性值为中文，必须翻译成英文。\n2. 基础词来自 productName 的核心名词，空格转中横线。\n3. 按 attributes 顺序追加非空的 value（翻译成英文），单位规范化：如 \"2 inch\"→\"2inch\"、\"10 mm\"→\"10mm\"。\n4. 若 attributes 全为空，则可从图片描述提取 1–2 个英文关键词补充。\n5. 输出约束：只输出一条 slug，字符仅限 a–z、0–9 和 `-`。\n6. 如果没有收集到商品，根据在售商品随机挑选生成查询\n\n示例（仅演示格式，不限品类）：\n- productName: \"古巴链cuban chain\"，value: \"2inch\"  \n  输出: `cuban-chain-2inch`\n- productName: \"玫瑰金定制字母Y吊坠\"，无属性，图片含 \"custom letter y pendant\"  \n  输出: `rose-gold-letter-y-pendant`\n- productName: \"红色运动鞋\"，value: \"42\"  \n  输出: `sneakers-red-42`\n- productName: \"实木餐桌\"，value: \"2米\"  \n  输出: `wooden-dining-table-2m`")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode4_product_names_list", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode10_text")
.build();
stateGraph.addNode("LLMNode10", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode10.apply(state);
	String key = "LLMNode10_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [1756029554923] ——
KnowledgeRetrievalNode knowledgeRetrievalNode1 = KnowledgeRetrievalNode.builder()
.inputKey("LLMNode10_text")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-small")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.2)
.outputKey("knowledgeRetrievalNode1_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode1", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode1_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode1.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— LlmNode [1756030383240] ——
LlmNode LLMNode11 = LlmNode.builder()
.systemPromptTemplate("根据上传图片 {conversation_image_analysis} 和收集的客户意向商品 {conversation_collect_product_info_str}，生成用于知识库检索的中文关键词。\n\n规则：\n输出中文关键词，使用中文“、”分隔。\n只提取商品名称、类别、属性，不要模糊描述或回答问题。\n优先级：图片描述 > 意向商品（非空） > 上下文。\n若没有收集到商品，可从在售商品 {codeNode4_product_names_list} 随机挑选生成关键词。\n关键词必须具体，不要包含完整句子或冗余信息。\n\n示例：\n商品名称: 古巴链cuban chain，属性值: 2inch\n输出: cuban chain、2inch\n商品名称: 红色运动鞋 sport shose，属性值: 42，属性值：red\n输出: sport shose、red、42码\n\n要求:\n每条关键词应尽量覆盖商品名称和核心属性。\n禁止生成任何问答或解释，只输出关键词列表。")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode4_product_names_list", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode11_text")
.build();
stateGraph.addNode("LLMNode11", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode11.apply(state);
	String key = "LLMNode11_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [1756030505337] ——
KnowledgeRetrievalNode knowledgeRetrievalNode2 = KnowledgeRetrievalNode.builder()
.inputKey("LLMNode11_text")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-small")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.2)
.outputKey("knowledgeRetrievalNode2_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode2", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode2_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode2.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— LlmNode [1756087090945] ——
LlmNode LLMNode12 = LlmNode.builder()
.systemPromptTemplate("根据上传图片{conversation_image_analysis}和客户意向商品{conversation_collect_product_info_str}，判断销售阶段（陌生、兴趣、信任、意向、订单、成交、复购），提炼客户关注的核心问题或词语，并生成向量检索字段。 禁止回答客户问题。 输出格式：阶段: <阶段> | 客户提问: {sys_query}")
.params(Map.of("conversation_collect_product_info_str", "null", "sys_query", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode12_text")
.build();
stateGraph.addNode("LLMNode12", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode12.apply(state);
	String key = "LLMNode12_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [1756087097420] ——
KnowledgeRetrievalNode knowledgeRetrievalNode3 = KnowledgeRetrievalNode.builder()
.inputKey("LLMNode12_text")
.topK(6)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-small")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.8)
.outputKey("knowledgeRetrievalNode3_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode3", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode3_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode3.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— HttpNode [1756089028563] ——
HttpNode httpNode10 = HttpNode.builder()
.url("{{#env.test#}}/api/mind/query/{{#1755760119759.lessee#}}")
.retryConfig(new HttpNode.RetryConfig(3, 100, true))
.outputKey("httpNode10_body")
.build();
stateGraph.addNode("httpNode10", AsyncNodeAction.node_async(state -> {
   String varName = "httpNode10";
   String key = varName + "_body";
   Map<String, Object> result = httpNode10.apply(state);
   Object object = result.get(key);
   if(!(object instanceof Map<?, ?> map)) {
       return Map.of();
   }
   return Map.of(varName + "_headers", map.get("headers"), key, map.get("body"),
       varName + "_status_code", map.get("status"));
}
));

// —— CodeNode [1756089146690] ——
CodeExecutorNodeAction codeNode12 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: str) -> dict:
    try:
        data = json.loads(arg1).get("data", {})
    except json.JSONDecodeError:
        data = {}

    result = {
        "totalScore": data.get("totalScore", 50),
        "round": data.get("round", 5),
        "roundScore": data.get("roundScore", 10),
        "productScore": data.get("productScore", 10),
        "conclusion": data.get("endStatement")
        or "I'm carefully handling it now. It may take just a moment, and I truly appreciate your patience.",
    }

    return result
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "httpNode10_body"))
.outputKey("codeNode12_output")
    .build();
stateGraph.addNode("codeNode12", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode12.apply(state);
            String key = "codeNode12_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode12_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— AssignerNode [17560916613730] ——
AssignerNode assignerNode2 = AssignerNode.builder()
.addItem("conversation_sceneId", "codeNode15_id", AssignerNode.WriteMode.OVER_WRITE)
.addItem("conversation_stop_hosting", "codeNode15_stopHosting", AssignerNode.WriteMode.OVER_WRITE)
.build();
stateGraph.addNode("assignerNode2", AsyncNodeAction.node_async(assignerNode2));

// —— AssignerNode [1756104138927] ——
AssignerNode assignerNode3 = AssignerNode.builder()
.addItem("conversation_image_analysis", "LLMNode9_structured_output", AssignerNode.WriteMode.OVER_WRITE)
.build();
stateGraph.addNode("assignerNode3", AsyncNodeAction.node_async(assignerNode3));

// —— CodeNode [1756114974542] ——
CodeExecutorNodeAction codeNode13 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: dict) -> dict:
    if not arg1:
        return {
            "customerInfo": "",
            "customerDemand": "",
            "otherInfo": ""
        }
    return {
        "customerInfo": arg1.get("customerInfo", ""),
        "customerDemand": arg1.get("customerDemand", ""),
        "otherInfo": arg1.get("otherInfo", "")
    }

""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "LLMNode2_structured_output"))
.outputKey("codeNode13_output")
    .build();
stateGraph.addNode("codeNode13", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode13.apply(state);
            String key = "codeNode13_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode13_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— BranchNode [1756178976150] ——
stateGraph.addNode("branchNode3", AsyncNodeAction.node_async(state -> Map.of()));

// —— CodeNode [17562101662250] ——
CodeExecutorNodeAction codeNode14 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: list[dict]) -> dict:
    if not arg1:
        return {"result": ""}
    result_list = []
    for idx, item in enumerate(arg1, start=1):
        content = item.get("content", "").strip()
        score = item.get("metadata", {}).get("score", 0)

        item_str = f"{idx}. 内容: {content}; 相似度: {score:.3f}"
        result_list.append(item_str)

    result_str = "\\n".join(result_list)

    return {"result": result_str}
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "knowledgeRetrievalNode3_result"))
.outputKey("codeNode14_output")
    .build();
stateGraph.addNode("codeNode14", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode14.apply(state);
            String key = "codeNode14_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode14_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— CodeNode [1756450218887] ——
CodeExecutorNodeAction codeNode15 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: list) -> dict:
    if not arg1 or not isinstance(arg1, list):
        return {
            "id": "",
            "stopHosting": "0",
            "reason": ""
        }

    first_item = arg1[0]
    return {
        "id": str(first_item.get("id", "")),
        "stopHosting": first_item.get("stopHosting", "0"),
        "reason": first_item.get("reason", "")
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "LLMNode1_structured_output"))
.outputKey("codeNode15_output")
    .build();
stateGraph.addNode("codeNode15", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode15.apply(state);
            String key = "codeNode15_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode15_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— AssignerNode [1756451387126] ——
AssignerNode assignerNode4 = AssignerNode.builder()
.addItem("conversation_sceneId", null, AssignerNode.WriteMode.OVER_WRITE)
.build();
stateGraph.addNode("assignerNode4", AsyncNodeAction.node_async(assignerNode4));

// —— BranchNode [1756454234375] ——
stateGraph.addNode("branchNode4", AsyncNodeAction.node_async(state -> Map.of()));

// —— LlmNode [1756870955241] ——
LlmNode LLMNode13 = LlmNode.builder()
.systemPromptTemplate("任务：根据输入的商品信息和图片描述，生成一个**全英文**、**单行**、**小写**、**中横线分隔（kebab-case）**的搜索查询词。禁止输出任何解释或多余内容，只输出最终 slug。\n\n输入：\n- 商品信息：{conversation_collect_product_info_str}\n- 图片描述：{conversation_image_analysis}\n\n规则：\n1. 若 productName 或属性值为中文，必须翻译成英文。\n2. 基础词来自 productName 的核心名词，空格转中横线。\n3. 按 attributes 顺序追加非空的 value（翻译成英文），单位规范化：如 \"2 inch\"→\"2inch\"、\"10 mm\"→\"10mm\"。\n4. 若 attributes 全为空，则可从图片描述提取 1–2 个英文关键词补充。\n5. 输出约束：只输出一条 slug，字符仅限 a–z、0–9 和 `-`。\n6.查询语句放在keywords字段里面\n7.严格输出json格式\n\n示例（仅演示格式，不限品类）：\n- productName: \"古巴链cuban chain\"，value: \"2inch\"  \n  输出: `cuban-chain-2inch`\n- productName: \"玫瑰金定制字母Y吊坠\"，无属性，图片含 \"custom letter y pendant\"  \n  输出: `rose-gold-letter-y-pendant`\n- productName: \"红色运动鞋\"，value: \"42\"  \n  输出: `sneakers-red-42`\n- productName: \"实木餐桌\"，value: \"2米\"  \n  输出: `wooden-dining-table-2m`")
.params(Map.of("conversation_collect_product_info_str", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode13_text")
.build();
stateGraph.addNode("LLMNode13", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode13.apply(state);
	String key = "LLMNode13_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [1756871219944] ——
KnowledgeRetrievalNode knowledgeRetrievalNode4 = KnowledgeRetrievalNode.builder()
.inputKey("sys_query")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-small")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.8)
.outputKey("knowledgeRetrievalNode4_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode4", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode4_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode4.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— KnowledgeRetrievalNode [1756871225455] ——
KnowledgeRetrievalNode knowledgeRetrievalNode5 = KnowledgeRetrievalNode.builder()
.inputKey("null")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-small")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.2)
.outputKey("knowledgeRetrievalNode5_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode5", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode5_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode5.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— LlmNode [1756871378233] ——
LlmNode LLMNode14 = LlmNode.builder()
.systemPromptTemplate("You are a product quotation assistant. Based on customer info and product database, reply with an English quotation and product link.\n\nInput:\n- Collected product info: {conversation_collect_product_info_str}\n- Product database: {{#context#}}\n- Merchant rules: {codeNode3_general_rules_str}\n- Sales strategy: {codeNode14_result}\n- Search keywords: {structured_output.keywords}\n\nRequirements:\n1. Reply in English, ≤30 characters (spaces + punctuation). Links not counted.\n2. First sentence = core quotation.\n3. End each sentence with .?! and separate by ###.\n4. Only output database product links, each ending with ###.\n5. If similarity ≥40%, quote best match price + link. Else, give price range (low–high).\n6. Use highest similarity product attributes when quoting.\n7. Do not ask for provided attributes; only ask for missing ones.\n8. No repeated questions, no fabricated links, no outside products.")
.params(Map.of("codeNode3_general_rules_str", "null", "conversation_collect_product_info_str", "null", "codeNode14_result", "null"))
.chatClient(chatClient)
.outputKey("LLMNode14_text")
.build();
stateGraph.addNode("LLMNode14", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode14.apply(state);
	String key = "LLMNode14_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [17573828773850] ——
KnowledgeRetrievalNode knowledgeRetrievalNode6 = KnowledgeRetrievalNode.builder()
.inputKey("sys_query")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-small")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.8)
.outputKey("knowledgeRetrievalNode6_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode6", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode6_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode6.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— KnowledgeRetrievalNode [17573828917380] ——
KnowledgeRetrievalNode knowledgeRetrievalNode7 = KnowledgeRetrievalNode.builder()
.inputKey("sys_query")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-small")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.8)
.outputKey("knowledgeRetrievalNode7_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode7", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode7_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode7.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— LlmNode [1757382975020] ——
LlmNode LLMNode15 = LlmNode.builder()
.systemPromptTemplate("你是一个专业的【销售话术整合助手】。\n你的目标是：基于提供的销售思维与销售话术，结合客户提出的问题或需求，生成合适的回复，既要体现专业性，又要促成成交。\n输入信息：\n销售思维：{codeNode10_result}\n报价/销售话术：{{#context#}}\n用户问题或反馈：{{#user_input#}}\n输出要求：\n回答必须结合“销售思维”中的逻辑和立场。\n融合“销售话术”中的内容，语言自然顺畅。\n输出的语气要：真诚、专业、引导成交。\n在满足客户疑问的同时，适度加入价值塑造和成交推进。\n每次回答需简洁有力（少于40字），避免啰嗦。")
.params(Map.of("codeNode10_result", "null"))
.chatClient(chatClient)
.outputKey("LLMNode15_text")
.build();
stateGraph.addNode("LLMNode15", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode15.apply(state);
	String key = "LLMNode15_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— LlmNode [1757644567098] ——
LlmNode LLMNode16 = LlmNode.builder()
.systemPromptTemplate("你是一名高情商、专业且善于共情的心理倾听者，同时在销售场景中担任可信赖顾问。\n你的目标是与客户建立情感连接，取得客户信任，并在合适时推进销售。\n\n基本规则\n1. 回复内容必须使用英文。\n2. 优先共情，理解用户情绪、关注点和背景，适时鼓励、肯定或安慰。\n3. 根据用户话语判断当前状态：\n   - “继续共情”：用户仍在表达顾虑、情绪或犹豫，尚未显示购买意向。\n   - “推进销售”：用户表现出兴趣、购买意向或对你/你的建议好奇，愿意接受下一步安排。\n\n触发“推进销售”的信号包括：\n- 用户主动透露更多个人或背景信息\n- 用户主动询问你的意见或建议\n- 用户表现出对你个人的好奇\n- 用户愿意接受你的下一步安排\n\n输入占位符\n- 用户问题：{sys_query}\n- 共情话术库参考：{{#context#}}\n\n任务要求\n1. 你必须只输出 JSON，不能包含任何多余的文字。\n\n示例输出：\n{\n  \"content\": \"It sounds like you're going through a tough moment, and I’m here to support you.\",\n  \"reason\": \"用户在表达负面情绪，没有表现出购买意向，因此需要继续共情。\",\n  \"tag\": \"继续共情\"\n}")
.params(Map.of("sys_query", "null"))
.chatClient(chatClient)
.outputKey("LLMNode16_text")
.build();
stateGraph.addNode("LLMNode16", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode16.apply(state);
	String key = "LLMNode16_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— CodeNode [1757651036593] ——
CodeExecutorNodeAction codeNode16 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
def main(arg1: list[dict]) -> dict:
    quotable_products = [
        item["productName"] for item in arg1 if item.get("isQuotable") == 1
    ]

    return {"quotableProductNames": quotable_products}

""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "conversation_collect_product_info"))
.outputKey("codeNode16_output")
    .build();
stateGraph.addNode("codeNode16", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode16.apply(state);
            String key = "codeNode16_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode16_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— BranchNode [1757657087454] ——
stateGraph.addNode("branchNode5", AsyncNodeAction.node_async(state -> Map.of()));

// —— LlmNode [1757677324196] ——
LlmNode LLMNode17 = LlmNode.builder()
.systemPromptTemplate("You are a product information assistant.\n\nInput:\n- Customer product info: {conversation_collect_product_info_str}\n- Reference sales script: {LLMNode19_text}\n- Merchant rules: {codeNode3_general_rules_str}\n- Sales strategy reference: {codeNode10_result}\n- Product database links: {{#context#}}\n\nTask:\n1. Only ask about customer info fields that are empty, in order.\n2. Skip attributes already provided; do not repeat questions.\n3. Provide at least 5 relevant product links, each ending with ###.\n4. Reply in English, concise (<30 words, links not counted), direct.\n5. Core question/info in the first sentence.\n6. End each sentence with . ? ! and add ### after each.\n7. Do not provide any price or quotation.\n8. Follow merchant rules strictly.\n\nProhibitions:\n- >40 words (links not counted)\n- Incomplete sentences\n- Consecutive questions\n- Repeated or similar meanings")
.params(Map.of("codeNode3_general_rules_str", "null", "conversation_collect_product_info_str", "null", "codeNode10_result", "null", "LLMNode19_text", "null"))
.chatClient(chatClient)
.outputKey("LLMNode17_text")
.build();
stateGraph.addNode("LLMNode17", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode17.apply(state);
	String key = "LLMNode17_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— BranchNode [1758018112991] ——
stateGraph.addNode("branchNode6", AsyncNodeAction.node_async(state -> Map.of()));

// —— LlmNode [1758018138578] ——
LlmNode LLMNode18 = LlmNode.builder()
.systemPromptTemplate("你是一名高情商、专业且善于共情的心理倾听者，同时在销售场景中担任可信赖顾问。\n当前对话已经判定为“推进销售”，你的任务是生成一条过渡语句，自然将对话从共情过渡到询问客户是否对意向商品感兴趣或需要购买。\n\n【输入信息】\n角色设定：{codeNode1_character_design_str}\n在售商品列表：{codeNode4_product_names_str}\n客户意向商品：{conversation_collect_product_info_str}\n\n【输出要求】\n回复必须使用英文。\n保持角色特征，语气自然、友好、专业。\n尽量包含对客户之前情绪、背景或兴趣的呼应，让过渡显得自然。\nmessage字段用 ### 分句，不要整段文字。\n严格只输出 JSON，不能有任何额外文字。\n\n示例：\n{\n  \"reason\": \"客户正在分享他们的想法和感受，因此理解这一点能够自然而然地建立起沟通的桥梁。通过逐渐将焦点转移到产品上，我们尊重他们的情绪，同时引导他们关注他们的兴趣所在。\",\n  \"message\": \"I can see how much thought you’ve been putting into this, and that’s really important—would you like to explore how this piece might fit what you’ve been looking for?\"\n}")
.params(Map.of("conversation_collect_product_info_str", "null", "codeNode1_character_design_str", "null", "codeNode4_product_names_str", "null"))
.chatClient(chatClient)
.outputKey("LLMNode18_text")
.build();
stateGraph.addNode("LLMNode18", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode18.apply(state);
	String key = "LLMNode18_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— LlmNode [1758189714741] ——
LlmNode LLMNode19 = LlmNode.builder()
.systemPromptTemplate("你是一个专业的【销售报价话术整合助手】。\n目标：基于提供的销售思维与报价话术，结合用户的提问或反馈，生成合适的销售回复。\n输入信息：\n销售思维：{codeNode10_result}\n报价话术：{{#context#}}\n用户输入：{{#user_input#}}\n输出要求：\n回复必须体现销售思维中的逻辑与立场。\n自然融合报价话术，语言要顺畅且有说服力。\n语气保持：专业、真诚、积极引导成交。\n既要满足客户疑问，又要塑造价值，推动成交。\n回复简洁有力（少于40字），避免冗长。")
.params(Map.of("codeNode10_result", "null"))
.chatClient(chatClient)
.outputKey("LLMNode19_text")
.build();
stateGraph.addNode("LLMNode19", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode19.apply(state);
	String key = "LLMNode19_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— VariableAggregatorNode [1758191872887] ——
VariableAggregatorNode variableAggregatorNode1 = VariableAggregatorNode.builder()
    .variables(List.of(List.of("codeNode17_result"), List.of("LLMNode14_text"), List.of("LLMNode17_text"), List.of("LLMNode6_text")))
    .outputKey("variableAggregatorNode1_output")
    .outputType("list")
    .build();
stateGraph.addNode("variableAggregatorNode1", AsyncNodeAction.node_async((state) -> {
	Map<String, Object> result = variableAggregatorNode1.apply(state);
	String key = "variableAggregatorNode1_output";
	Object object = result.get(key);
	if (object instanceof List<?> list) {
		return Map.of(key, list.isEmpty() ? "unknown" : list.get(0));
	} else {
		return Map.of(key, object == null ? "unknown" : object.toString());
	}
}
));

// —— AnswerNode [1758191904312] ——
AnswerNode answerNode2 = AnswerNode.builder()
.answer("<think>{codeNode10_result}</think>\n{variableAggregatorNode1_output}")
.outputKey("answerNode2_answer")
.build();
stateGraph.addNode("answerNode2", AsyncNodeAction.node_async(answerNode2));

stateGraph.addEdge("answerNode2", END);
// —— CodeNode [17581921212590] ——
CodeExecutorNodeAction codeNode17 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""

def main(arg1: dict) -> dict:
    return {
        "result": arg1.get("message", ""),
    }

""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "variableAggregatorNode2_output"))
.outputKey("codeNode17_output")
    .build();
stateGraph.addNode("codeNode17", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode17.apply(state);
            String key = "codeNode17_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode17_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— VariableAggregatorNode [1758194021802] ——
VariableAggregatorNode variableAggregatorNode2 = VariableAggregatorNode.builder()
    .variables(List.of(List.of("LLMNode18_structured_output"), List.of("LLMNode16_structured_output")))
    .outputKey("variableAggregatorNode2_output")
    .outputType("list")
    .build();
stateGraph.addNode("variableAggregatorNode2", AsyncNodeAction.node_async((state) -> {
	Map<String, Object> result = variableAggregatorNode2.apply(state);
	String key = "variableAggregatorNode2_output";
	Object object = result.get(key);
	if (object instanceof List<?> list) {
		return Map.of(key, list.isEmpty() ? "unknown" : list.get(0));
	} else {
		return Map.of(key, object == null ? "unknown" : object.toString());
	}
}
));

// —— AssignerNode [1758200967258] ——
AssignerNode assignerNode5 = AssignerNode.builder()
.addItem("conversation_sceneId", null, AssignerNode.WriteMode.OVER_WRITE)
.build();
stateGraph.addNode("assignerNode5", AsyncNodeAction.node_async(assignerNode5));

// —— LlmNode [17582503748420] ——
LlmNode LLMNode20 = LlmNode.builder()
.systemPromptTemplate("任务：根据输入的商品信息和图片描述，生成一个**全英文**、**单行**、**小写**、**中横线分隔（kebab-case）**的搜索查询词。禁止输出任何解释或多余内容，只输出最终 slug。\n\n输入：\n- 商品信息：{conversation_collect_product_info_str}\n- 图片描述：{conversation_image_analysis}\n\n规则：\n1. 若 productName 或属性值为中文，必须翻译成英文。\n2. 基础词来自 productName 的核心名词，空格转中横线。\n3. 按 attributes 顺序追加非空的 value（翻译成英文），单位规范化：如 \"2 inch\"→\"2inch\"、\"10 mm\"→\"10mm\"。\n4. 若 attributes 全为空，则可从图片描述提取 1–2 个英文关键词补充。\n5. 输出约束：只输出一条 slug，字符仅限 a–z、0–9 和 `-`。\n\n示例（仅演示格式，不限品类）：\n- productName: \"古巴链cuban chain\"，value: \"2inch\"  \n  输出: `cuban-chain-2inch`\n- productName: \"玫瑰金定制字母Y吊坠\"，无属性，图片含 \"custom letter y pendant\"  \n  输出: `rose-gold-letter-y-pendant`\n- productName: \"红色运动鞋\"，value: \"42\"  \n  输出: `sneakers-red-42`\n- productName: \"实木餐桌\"，value: \"2米\"  \n  输出: `wooden-dining-table-2m`")
.params(Map.of("conversation_collect_product_info_str", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode20_text")
.build();
stateGraph.addNode("LLMNode20", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode20.apply(state);
	String key = "LLMNode20_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [17582503774360] ——
KnowledgeRetrievalNode knowledgeRetrievalNode8 = KnowledgeRetrievalNode.builder()
.inputKey("LLMNode20_text")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-small")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.2)
.outputKey("knowledgeRetrievalNode8_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode8", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode8_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode8.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— LlmNode [17582503829810] ——
LlmNode LLMNode21 = LlmNode.builder()
.systemPromptTemplate("根据上传图片 {conversation_image_analysis} 和收集的客户意向商品 {conversation_collect_product_info_str} 生成知识库检索关键词（禁止回答问题）。\n\n规则：\n输出中文关键词，用中文顿号 分隔，提取商品名称、类别或属性，不要模糊描述。\n优先级： 图片描述 > 意向商品（若 value 不为空）> 上下文。若无法提取任何关键词，输出“无”。\n示例（多品类，文本格式）：\n商品名称: 古巴链cuban chain，属性值: 2寸\n输出: 古巴链、2寸\n商品名称: 红色运动鞋，属性值: 42、红色\n输出: 运动鞋、红色、42码\n商品名称: 实木餐桌，属性值: 2米、实木\n输出: 餐桌、实木、2米\n商品名称: 蓝牙耳机，属性值: 黑色\n输出: 蓝牙耳机、黑色\n无意向商品，图片描述: 绿色沙发\n输出: 沙发、绿色")
.params(Map.of("conversation_collect_product_info_str", "null", "conversation_image_analysis", "null"))
.chatClient(chatClient)
.outputKey("LLMNode21_text")
.build();
stateGraph.addNode("LLMNode21", AsyncNodeAction.node_async(state -> {
	Map<String, Object> result = LLMNode21.apply(state);
	String key = "LLMNode21_text";
	Object object = result.get(key);
	if(object instanceof AssistantMessage && ((AssistantMessage) object).getText() != null) {
		return Map.of(key, ((AssistantMessage) object).getText());
	}
	return Map.of(key, object != null ? object.toString() : "unknown");
}
));

// —— KnowledgeRetrievalNode [17582504454200] ——
KnowledgeRetrievalNode knowledgeRetrievalNode9 = KnowledgeRetrievalNode.builder()
.inputKey("LLMNode21_text")
.topK(10)
.enableRanker(false)
.retrievalMode("multiple")
.embeddingModelName("text-embedding-3-small")
.embeddingProviderName("langgenius/openai/openai")
.vectorWeight(0.2)
.outputKey("knowledgeRetrievalNode9_result")
.vectorStore(vectorStore)
.isKeyFirst(false).build();
stateGraph.addNode("knowledgeRetrievalNode9", AsyncNodeAction.node_async((state) -> {
	String key = "knowledgeRetrievalNode9_result";
	// 将结果转换为Dify工作流中需要的变量
	Map<String, Object> result = knowledgeRetrievalNode9.apply(state);
	Object object = result.get(key);
	if(object instanceof List<?> list && !list.isEmpty() && list.get(0) instanceof Document) {
		// 返回值为Array[Object]（用List<Map>）
		List<Document> documentList = (List<Document>) list;
		List<Map<String, Object>> mapList = documentList.stream().map(document ->
						Map.of("content", document.getFormattedContent(), "title", document.getId(), "url", "", "icon", "", "metadata", document.getMetadata()))
				.toList();
		return Map.of(key, mapList);
	} else {
		return Map.of(key, List.of(Map.of("content", object.toString(), "title", "unknown", "url", "unknown", "icon", "unknown", "metadata", object)));
	}
}
));

// —— CodeNode [1758250743153] ——
CodeExecutorNodeAction codeNode18 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
import re 

def main(arg1: list[dict], arg2: list[dict]) -> dict:
    threshold = 0.15

    merged = arg1 + arg2

    filtered_sorted = sorted(
        [
            item
            for item in merged
            if item.get("metadata", {}).get("score", 0) >= threshold
        ],
        key=lambda x: x.get("metadata", {}).get("score", 0),
        reverse=True,
    )

    result_count = len(filtered_sorted)

    url_pattern = re.compile(r'https?://[^\\s"\\']+')
    links = []
    for item in filtered_sorted:
        content = item.get("content", "")
        matches = url_pattern.findall(content)
        if matches:
            links.extend(matches)

    return {
        "result": filtered_sorted,
        "count": result_count,
        "links": list(set(links)),
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "knowledgeRetrievalNode2_result", "arg2", "knowledgeRetrievalNode1_result"))
.outputKey("codeNode18_output")
    .build();
stateGraph.addNode("codeNode18", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode18.apply(state);
            String key = "codeNode18_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode18_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— CodeNode [17582512538170] ——
CodeExecutorNodeAction codeNode19 = CodeExecutorNodeAction.builder()
    .codeExecutor(codeExecutor)  // 注入的 CodeExecutor Bean
    .codeLanguage("python3")
    .code("""
import re 

def main(arg1: list[dict], arg2: list[dict]) -> dict:
    threshold = 0.15

    merged = arg1 + arg2

    filtered_sorted = sorted(
        [
            item
            for item in merged
            if item.get("metadata", {}).get("score", 0) >= threshold
        ],
        key=lambda x: x.get("metadata", {}).get("score", 0),
        reverse=True,
    )

    result_count = len(filtered_sorted)

    url_pattern = re.compile(r'https?://[^\\s"\\']+')
    links = []
    for item in filtered_sorted:
        content = item.get("content", "")
        matches = url_pattern.findall(content)
        if matches:
            links.extend(matches)

    return {
        "result": filtered_sorted,
        "count": result_count,
        "links": list(set(links)),
    }
""")
    .config(codeExecutionConfig)  // 注入的 CodeExecutionConfig Bean
    .params(Map.of("arg1", "knowledgeRetrievalNode8_result", "arg2", "knowledgeRetrievalNode9_result"))
.outputKey("codeNode19_output")
    .build();
stateGraph.addNode("codeNode19", AsyncNodeAction.node_async((state) -> {
            // 将代码运行的结果拆包
            Map<String, Object> result = codeNode19.apply(state);
            String key = "codeNode19_output";
            Object object = result.get(key);
            if(!(object instanceof Map)) {
            	return Map.of();
            }
            return ((Map<String, Object>) object).entrySet().stream()
            		.collect(Collectors.toMap(
                    	entry -> "codeNode19_" + entry.getKey(),
                    	Map.Entry::getValue
                    ));
        }
));

// —— QuestionClassifierNode [1758281118514] ——
QuestionClassifierNode questionClassifyNode1 = QuestionClassifierNode.builder()
.chatClient(chatClient)
.inputTextKey("sys_query")
.categories(List.of("共情\n用户消息与商品或销售无关，需要共情回应，例如打招呼、闲聊、表达情绪等\n", "报价\n用户明确询问价格，且我们已有完整商品信息", "商品收集\n\n用户对商品有兴趣，但提供的信息不完整，需要进一步收集信息，例如提到商品名称、型号、颜色、尺寸等，但未提供足够信息以报价。"))
.outputKey("questionClassifyNode1_class_name")
.classificationInstructions(List.of("请根据输入内容选择对应分类"))
.build();
stateGraph.addNode("questionClassifyNode1", AsyncNodeAction.node_async(questionClassifyNode1));


        // add edges
        stateGraph.addEdge("httpNode1", "codeNode1");
stateGraph.addEdge("httpNode2", "codeNode2");
stateGraph.addEdge("httpNode3", "codeNode3");
stateGraph.addEdge("httpNode4", "codeNode4");
stateGraph.addEdge("httpNode5", "codeNode5");
stateGraph.addEdge("httpNode8", "LLMNode3");
stateGraph.addEdge(START, "httpNode1");
stateGraph.addEdge(START, "httpNode2");
stateGraph.addEdge(START, "httpNode3");
stateGraph.addEdge(START, "httpNode4");
stateGraph.addEdge(START, "httpNode5");
stateGraph.addEdge(START, "httpNode7");
stateGraph.addEdge("httpNode7", "codeNode6");
stateGraph.addEdge("LLMNode3", "codeNode7");
stateGraph.addEdge("codeNode7", "httpNode9");
stateGraph.addEdge("LLMNode7", "codeNode10");
stateGraph.addEdge("codeNode11", "branchNode2");
stateGraph.addEdge("LLMNode8", "answerNode1");
stateGraph.addEdge("codeNode1", "branchNode1");
stateGraph.addEdge("codeNode2", "branchNode1");
stateGraph.addEdge("codeNode3", "branchNode1");
stateGraph.addEdge("codeNode4", "branchNode1");
stateGraph.addEdge("codeNode5", "branchNode1");
stateGraph.addEdge("codeNode6", "branchNode1");
stateGraph.addEdge("LLMNode12", "knowledgeRetrievalNode3");
stateGraph.addEdge(START, "httpNode10");
stateGraph.addEdge("httpNode10", "codeNode12");
stateGraph.addEdge("codeNode12", "branchNode1");
stateGraph.addEdge("LLMNode9", "assignerNode3");
stateGraph.addEdge("LLMNode4", "codeNode9");
stateGraph.addEdge("LLMNode2", "codeNode13");
stateGraph.addEdge("codeNode13", "httpNode8");
stateGraph.addEdge("knowledgeRetrievalNode3", "codeNode14");
stateGraph.addEdge("LLMNode11", "knowledgeRetrievalNode2");
stateGraph.addEdge("LLMNode10", "knowledgeRetrievalNode1");
stateGraph.addEdge("answerNode1", "httpNode6");
stateGraph.addEdge("codeNode15", "assignerNode2");
stateGraph.addEdge("LLMNode1", "codeNode15");
stateGraph.addEdge("assignerNode4", "LLMNode8");
stateGraph.addEdge("LLMNode15", "LLMNode6");
stateGraph.addEdge("codeNode16", "branchNode5");
stateGraph.addEdge("LLMNode5", "codeNode8");
stateGraph.addEdge("codeNode8", "LLMNode4");
stateGraph.addEdge("codeNode9", "assignerNode1");
stateGraph.addEdge("assignerNode3", "LLMNode5");
stateGraph.addEdge("knowledgeRetrievalNode7", "LLMNode16");
stateGraph.addEdge("knowledgeRetrievalNode5", "LLMNode14");
stateGraph.addEdge("knowledgeRetrievalNode6", "LLMNode15");
stateGraph.addEdge("LLMNode13", "knowledgeRetrievalNode5");
stateGraph.addEdge("knowledgeRetrievalNode4", "LLMNode19");
stateGraph.addEdge("LLMNode19", "LLMNode17");
stateGraph.addEdge("LLMNode14", "variableAggregatorNode1");
stateGraph.addEdge("LLMNode17", "variableAggregatorNode1");
stateGraph.addEdge("LLMNode6", "variableAggregatorNode1");
stateGraph.addEdge("codeNode17", "variableAggregatorNode1");
stateGraph.addEdge("variableAggregatorNode1", "codeNode11");
stateGraph.addEdge("answerNode2", "branchNode4");
stateGraph.addEdge("LLMNode16", "branchNode6");
stateGraph.addEdge("LLMNode18", "variableAggregatorNode2");
stateGraph.addEdge("variableAggregatorNode2", "codeNode17");
stateGraph.addEdge("httpNode6", "assignerNode5");
stateGraph.addEdge("assignerNode5", "LLMNode2");
stateGraph.addEdge("LLMNode20", "knowledgeRetrievalNode8");
stateGraph.addEdge("LLMNode21", "knowledgeRetrievalNode9");
stateGraph.addEdge("knowledgeRetrievalNode1", "codeNode18");
stateGraph.addEdge("knowledgeRetrievalNode2", "codeNode18");
stateGraph.addEdge("codeNode18", "LLMNode6");
stateGraph.addEdge("knowledgeRetrievalNode8", "codeNode19");
stateGraph.addEdge("knowledgeRetrievalNode9", "codeNode19");
stateGraph.addEdge("codeNode19", "LLMNode17");
stateGraph.addEdge("assignerNode2", "questionClassifyNode1");
stateGraph.addEdge("codeNode10", "questionClassifyNode1");
stateGraph.addEdge("codeNode14", "questionClassifyNode1");
stateGraph.addEdge("assignerNode1", "questionClassifyNode1");
stateGraph.addConditionalEdges("questionClassifyNode1",
            edge_async(state -> {
String value = state.value("questionClassifyNode1_class_name", String.class).orElse("");
if (value.contains("报价 用户明确询问价格，且我们已有完整商品信息"))
    return "报价用户明确询问价格，且我们已有完整商品信息";
if (value.contains("共情 用户消息与商品或销售无关，需要共情回应，例如打招呼、闲聊、表达情绪等"))
    return "共情用户消息与商品或销售无关，需要共情回应，例如打招呼、闲聊、表达情绪等";
if (value.contains("商品收集 用户对商品有兴趣，但提供的信息不完整，需要进一步收集信息，例如提到商品名称、型号、颜色、尺寸等，但未提供足够信息以报价。"))
    return "商品收集用户对商品有兴趣，但提供的信息不完整，需要进一步收集信息，例如提到商品名称、型号、颜色、尺寸等，但未提供足够信息以报价。";
if (value.contains("商品收集用户对商品有兴趣，但提供的信息不完整，需要进一步收集信息，例如提到商品名称、型号、颜色、尺寸等，但未提供足够信息以报价。"))
    return "商品收集 用户对商品有兴趣，但提供的信息不完整，需要进一步收集信息，例如提到商品名称、型号、颜色、尺寸等，但未提供足够信息以报价。";
if (value.contains("商品收集用户对商品有兴趣，但提供的信息不完整，需要进一步收集信息，例如提到商品名称、型号、颜色、尺寸等，但未提供足够信息以报价。"))
    return "商品收集 用户对商品有兴趣，但提供的信息不完整，需要进一步收集信息，例如提到商品名称、型号、颜色、尺寸等，但未提供足够信息以报价。";
return "商品收集用户对商品有兴趣，但提供的信息不完整，需要进一步收集信息，例如提到商品名称、型号、颜色、尺寸等，但未提供足够信息以报价。";
            }),
            Map.of("报价用户明确询问价格，且我们已有完整商品信息", "codeNode16",
                    "共情用户消息与商品或销售无关，需要共情回应，例如打招呼、闲聊、表达情绪等 ", "knowledgeRetrievalNode7",
                    "商品收集用户对商品有兴趣，但提供的信息不完整，需要进一步收集信息，例如提到商品名称、型号、颜色、尺寸等，但未提供足够信息以报价。", "LLMNode10",
                    "商品收集 用户对商品有兴趣，但提供的信息不完整，需要进一步收集信息，例如提到商品名称、型号、颜色、尺寸等，但未提供足够信息以报价。", "LLMNode11",
                    "商品收集 用户对商品有兴趣，但提供的信息不完整，需要进一步收集信息，例如提到商品名称、型号、颜色、尺寸等，但未提供足够信息以报价。", "knowledgeRetrievalNode6")
);
stateGraph.addConditionalEdges("branchNode1", edge_async(state -> {
if((state.value("unknown", String.class).orElseThrow().equals("开始进行聊天分析"))) {
return "971d3615-1547-4353-93b4-8e584059f8a5";}
if((state.value("unknown", String.class).orElseThrow().equals("1"))) {
return "d2c7c93c-8c67-43cc-af54-d2783cbe7ddb";}
if((state.value("unknown", String.class).orElseThrow().contains("0"))) {
return "88cb6dd7-a196-4321-bc74-196331ef8a09";}
return "false";}), Map.of("971d3615-1547-4353-93b4-8e584059f8a5", "LLMNode2", "88cb6dd7-a196-4321-bc74-196331ef8a09", "LLMNode12"));
stateGraph.addConditionalEdges("branchNode6", edge_async(state -> {
if((state.value("LLMNode16_text", java.lang.String.class).orElseThrow().equals("推进销售"))) {
return "true";}
return "false";}), Map.of("false", "variableAggregatorNode2", "true", "LLMNode18"));
stateGraph.addConditionalEdges("branchNode2", edge_async(state -> {
if((state.value("codeNode11_result", String.class).orElseThrow().isEmpty())) {
return "true";}
return "false";}), Map.of("true", "answerNode2", "false", "assignerNode4"));
stateGraph.addConditionalEdges("branchNode4", edge_async(state -> {
if(!(state.value("unknown", String.class).orElseThrow().isEmpty())) {
return "true";}
return "false";}), Map.of("true", "httpNode6"));
stateGraph.addConditionalEdges("branchNode3", edge_async(state -> {
if(!(state.value("unknown", java.lang.String.class).orElseThrow().isEmpty())) {
return "true";}
return "false";}), Map.of("false", "LLMNode5", "true", "LLMNode9"));
stateGraph.addConditionalEdges("branchNode5", edge_async(state -> {
if(!(state.value("codeNode16_quotableProductNames", java.lang.String.class).orElseThrow().isEmpty())) {
return "true";}
return "false";}), Map.of("true", "LLMNode13", "false", "LLMNode21"));
//stateGraph;

        return stateGraph.compile();
    }

    @Bean
    public VectorStore customVectorStore(EmbeddingModel embeddingModel) {
        var chunks = new TokenTextSplitter().transform(new TextReader(ragSource).read());
        SimpleVectorStore vectorStore = SimpleVectorStore.builder(embeddingModel).build();
        vectorStore.write(chunks);
        return vectorStore;
    }

    @Bean
    public Path tempDir() throws IOException {
        // todo： set your work dir
        Path tempDir = Files.createTempDirectory("code-execution-workdir-");
        tempDir.toFile().deleteOnExit();
        return tempDir;
    }

    @Bean
    public CodeExecutionConfig codeExecutionConfig(Path tempDir) {
        return new CodeExecutionConfig().setWorkDir(tempDir.toString());
    }

    @Bean
    public CodeExecutor codeGenerator() {
        return new LocalCommandlineCodeExecutor();
    }
}
